﻿//Everyone

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;

namespace Tzync
{
    /*****************************************************************************/
    /* Facade Class of our Software. Allocates responsibilities to other classes */
    /*****************************************************************************/

    public class SyncTool
    {
        //Data Members of the SyncTool Class
        private Analyze analyzer;
        private MetaData metaDataHandler;
        private List<Difference> differences;
        private List<TreeNode> arrNodes;
        private SystemType systemName;
        private bool secondTime, firstTime;      
        private ISubject differenceSubject;
        private String cpuID, rootAddress;
        private ProfileSystem profileSystemHandler;
        private RollbackSystem rollbackSystemHandler;
        private Profile profile;
        private Filter filter;
        private SafeSync safeSync;
        private List<Difference> prevDifferences;

        public SyncTool(String newCpuId)
        {
            //Assigning local variables to Values from parameters
            cpuID = newCpuId;
            profileSystemHandler = new ProfileSystem(cpuID);
            rollbackSystemHandler = new RollbackSystem();

            arrNodes = new List<TreeNode>();
            firstTime = false;
            differenceSubject = new DifferenceSubject();

            //Setup safe sync object
            safeSync = new SafeSync();

            differences = new List<Difference>();
            prevDifferences = new List<Difference>();
        }

        //---------------------------------------------------------------------------Filters


        public FilterIncOrEx CheckIfAlreadyFiltered(string targetPath, FilterIncOrEx IncOrEx)
        {
            return filter.CheckIfAlreadyFiltered(targetPath, IncOrEx);
        }

        public void RemovePathFilter(string s)
        {
              filter.RemovePathFilter(s) ;
        }

        public void RemoveWildCardFilter(string s)
        {
            filter.RemoveWildCardFilter(s);
        }

        public void SetWildCardIncOrEx(FilterIncOrEx IncOrEx)
        {
            filter.WildCardIncOrEx = IncOrEx;
        }

        public FilterIncOrEx GetWildCardIncOrEx()
        {
            return filter.WildCardIncOrEx;
        }

        public void AddWildCardFilter(String s)
        {
            filter.AddWildCardFilter(s);
        }

        public void AddPathFilter(String s, FilterIncOrEx IncOrEx)
        {
            filter.AddPathFilter(s, IncOrEx);
        }

        public void ReadFromFilterFile()
        {
            filter.ReadFromFilterFile();
        }

        public void WriteToFilterFile()
        {
            filter.WriteToFilterFile();
        }

        public void FlushFilter()
        {
            filter.FlushAllFilters();
        }

        public List<string> GetIncludePathFilters()
        {
            return filter.GetIncludePathFilters();
        }

        public List<string> GetExcludePathFilters()
        {
            return filter.GetExcludePathFilters();
        }

        public List<string> GetWildCardFilters()
        {
            return filter.GetWildCardFilters();
        }

        public List<Difference> GetDifferences()
        {
            return differences;
        }

        public bool GetFirstTime()
        {
            return firstTime;
        }

        public bool GetSecondTime()
        {
            return secondTime;
        }        

        public List<TreeNode> GetArrNodes()
        {
            return this.arrNodes;
        }

        public ISubject GetDifferenceSubject()
        {
            return differenceSubject;
        }

        public void RegisterObserver(IObserver observer)
        {
            differenceSubject.RegisterObject(observer);     //register UI as an observerable object
        }

        public void CreateProfile(String newProfileName, String newRootAddress)
        {
            profile = profileSystemHandler.CreateProfile(newProfileName, newRootAddress);
            SetupSystemVariables();
            SetupRollback();
        }

        public List<String> GetAvailableProfiles()
        {
            return profileSystemHandler.GetAvailableProfiles();
        }

        public void WriteProfile()
        {
            profileSystemHandler.WriteProfile();
        }
                
        /// <summary>
        /// This function is called at the first sync at System B
        /// To add entries in SystemData in the profile
        /// </summary>
        /// <param name="p"></param>
        /// <param name="rootAddress"></param>
        public void ModifyProfile(Profile p, String rootAddress)
        {
            profile = profileSystemHandler.ModifyProfile(p, rootAddress);
            SetupSystemVariables();
            SetupRollback();
        }

        /// <summary>
        /// Sets the system variables and loads the profile
        /// </summary>
        /// <param name="newProfileName"></param>
        public void LoadProfile(String newProfileName)
        {
            profile = profileSystemHandler.GetProfile(newProfileName);
            SetupSystemVariables();
            LoadMetadata();
            SetupRollback();
        }

        //---------------------------------------------------------------------------Safe Sync
        public bool SafelySynchronized()
        {
            //Check if safely synced
            return safeSync.CheckSafelySynced();
        }

        public void PerformSafeSync()
        {
            profile = profileSystemHandler.GetProfile(safeSync.ProfileName);
            
            //update rollback
            rollbackSystemHandler.CurrentProfile = profile;
            rollbackSystemHandler.CurrentSystem = systemName;

            rollbackSystemHandler.LoadRollback();   //ask again
            safeSync.PerformSafeSync(rollbackSystemHandler);
            profile = null;
            rollbackSystemHandler = null;

            rollbackSystemHandler = new RollbackSystem();
        }

        /// <summary>
        /// Gets the system variables from profile info
        /// </summary>
        private void SetupSystemVariables()
        {
            //Setting local variables from parameters
            firstTime = profile.FirstSyncHost;
            secondTime = profile.FirstSyncGuest;
            systemName = profile.SystemName;

            //update rollback
            rollbackSystemHandler.CurrentProfile = profile;
            rollbackSystemHandler.CurrentSystem = systemName;

            if (systemName == SystemType.Host)
                rootAddress = profile.RootAddressHost;
            else
                rootAddress = profile.RootAddressGuest;

            safeSync.ProfileName = profile.ProfileName;

            //Filter Object initialization
            filter = new Filter(profile);
        }

        public Profile GetProfile()
        {
            return profile;
        }        
        /// <summary>
        /// Removes the profile and rollback information
        /// </summary>
        /// <param name="newProfileName">name of the profile to be removed.</param>
        public void RemoveProfile(String newProfileName)
        {
            profileSystemHandler.RemoveProfile(newProfileName);
            profileSystemHandler.WriteProfile();
            RemoveRollback(newProfileName);
        }

         /// <summary>
        /// This function is called when the root folder of the current job is moved/renamed
        /// </summary>
        /// <param name="path"></param>
        public void SetRootPath(String path)
        {
            if (systemName == SystemType.Host)
            {
                UpdateRollback(profile.RootAddressHost, path);
                arrNodes[0].SetFullPathHost(path);
                profile.RootAddressHost = path;
                profile.WriteSystem();
                rootAddress = path;

            }
            else if (systemName == SystemType.Guest)
            {
                UpdateRollback(profile.RootAddressGuest, path);
                arrNodes[0].SetFullPathGuest(path);
                profile.RootAddressGuest = path;
                profile.WriteSystem();
                rootAddress = path;
            }
        }        

        public void LoadMetadata()
        {
            //Loading Metadata            
            metaDataHandler = new MetaData(profile);
            metaDataHandler.LoadMetaData();
            arrNodes = metaDataHandler.getAllNodes();
           
            // to intialize the filter values
            ReadFromFilterFile();
        }        

        public void Analyze()
        {
            DirectoryInfo D = new DirectoryInfo(@rootAddress);
            analyzer = new Analyze(arrNodes, secondTime, systemName, differenceSubject, ref filter, profile.ProfilePath);
                        
            differences = analyzer.GetDifferences(D);            
        }

        public SystemType GetSystemName()
        {
            return systemName;
        }

        public String GetRootAddress()
        {
            return rootAddress;
        }

        public String GetCpuID()
        {
            return cpuID;
        }

        public void SetCpuID(String str)
        {
            cpuID = str;
        }

        public void Sync()
        {
            Sync sync;

            safeSync.Initialize();

            if (firstTime) // for first time syncing
            {
                if (differences == null || differences.Count == 0)
                {
                    DirectoryInfo source = new DirectoryInfo(rootAddress);
                    sync = new Sync(arrNodes, secondTime, systemName, profile.ProfilePath, safeSync);
                    sync.CopyFilesToHD(source);
                }
                else // user has synced and made changes. Then will only sync the differences
                {
                    sync = new Sync(differences, arrNodes, systemName, profile.ProfilePath, safeSync);
                    sync.Reconcile();
                }
                firstTime = false;
            }
            else if (secondTime) //copy files to system and then call analyze to copy changes
            {
                if (differences != null && differences.Count != 0)
                {
                    sync = new Sync(differences, arrNodes, systemName, profile.ProfilePath, safeSync);
                    sync.Reconcile();
                    sync.DeleteFilesForSecondTime();
                }
                else
                {
                    sync = new Sync(arrNodes, secondTime, systemName, profile.ProfilePath, safeSync);
                    sync.DeleteFilesForSecondTime();
                    sync.UpdateArrNodesForSecondTime(rootAddress);
                }
                secondTime = false;
            }
            else // normal syncing
            {
                rollbackSystemHandler.AddRollback(differences, arrNodes);  //adds to rollback files                
                sync = new Sync(differences, arrNodes, systemName, profile.ProfilePath, safeSync);                
                sync.Reconcile();
            }
            prevDifferences = differences;
            differences = null;          
            metaDataHandler.writeToFile();
        }

        //---------------------------------------------------------------------------Rollback

        /// <summary>
        /// Initializes rollback folders and metadata
        /// </summary>
        public void SetupRollback()
        {
            if (profile.HasHost && profile.HasGuest)
                rollbackSystemHandler.LoadRollback();

            else if (profile.HasHost && !profile.HasGuest)
                rollbackSystemHandler.CreateRollback();
        }

        /// <summary>
        /// Deletes rollback files of profiles deleted on other computer
        /// </summary>
        public void CleanupRollback()
        {
            rollbackSystemHandler.CleanupRollback(GetAvailableProfiles());
        }

        /// <summary>
        /// Performs rollback of a list of files
        /// </summary>
        /// <param name="PathList">Li</param>
        public void PerformRollback(List<String> PathList)
        {
            rollbackSystemHandler.PerformRollback(this, PathList, arrNodes);
            metaDataHandler.writeToFile();
        }

        /// <summary>
        /// Deletes rollback files of a deleted profile
        /// </summary>
        /// <param name="profileName"></param>
        public void RemoveRollback(String profileName)
        {
            rollbackSystemHandler.RemoveRollback(profileName);
        }

        /// <summary>
        /// Updates rollback with a new root address
        /// </summary>
        public void UpdateRollback(String oldRootAddress, String newRootAddress)
        {
            rollbackSystemHandler.UpdateRollbackFile(oldRootAddress, newRootAddress);
        }

        /// <summary>
        /// Returns a list of rollback files
        /// </summary>
        public List<RollbackFile> GetRollbackFileList()
        {
            return rollbackSystemHandler.CurrentRollback.RbFileList;
        }

        /// <summary>
        /// Returns space allocated to rollback on computer
        /// </summary>
        public long GetRollbackSpaceAllocated()
        {
            return rollbackSystemHandler.CurrentRollback.SpaceAllocated;
        }

        /// <summary>
        /// Returns space left for rollback on computer
        /// </summary>
        public long GetRollbackSpaceRemaining()
        {
            return rollbackSystemHandler.CurrentRollback.SpaceRemaining;
        }

        /// <summary>
        /// Resolves file/folder conflicts and writes to metadata.
        /// </summary>
        public void ResolveConflicts(List<Difference> conflicts)
        {
            rollbackSystemHandler.AddRollback(conflicts, arrNodes);  //adds to rollback files
            safeSync.Initialize();

            Sync sync;
            
            sync = new Sync(conflicts, arrNodes, systemName, profile.ProfilePath, safeSync);
            sync.Reconcile(prevDifferences);
            metaDataHandler.writeToFile();  //write to metadata

            List<Difference> newDifferences = new List<Difference>();
            if (differences == null) return;
            foreach (Difference diff in differences)
            {
                //MessageBox.Show(diff.GetPairType().Second.ToString() + diff.GetPartnerFile().GetName());
                if (diff.GetPairType().Second != OperationType.ModModConflict || diff.GetPairType().First != DirectionType.HDToSystem)
                //if (diff.GetPairType().First != DirectionType.HDToSystem)
                {
                    //MessageBox.Show(diff.GetPairType().Second.ToString());
                    newDifferences.Add(diff);
                }
            }
            differences = newDifferences;
        }

        public RollbackSystem RollbackSystemHandler
        {
            get { return rollbackSystemHandler; }
            set { rollbackSystemHandler = value; }
        }
    }

    /***********************/
    /* Class SyncTool ends */
    /***********************/
}
